/***********************************************************************************
 * 文件名： ptc.c
 * 作者： 刘言
 * 版本： 1
 * 说明：
 * 		PTC通信接口物理层协议实现、基础数据包格式的实现。仅支持主机模式。
 * 		PTC通信接口更多细节参考《PTC通信接口》文档。
 * 		本文件提供了以下接口：
 * 		PTC_Start:  开始通信
 * 		PTC_Stop: 结束通信
 * 		PTC_Send:  发送一个数据包
 * 		PTC_Receive:  接收一个数据包
 * 		PTC_TemporaryPower: 临时强供电
 * 		使用流程: 先调用 PTC_Start 如果成功说明已经与从机建立通信，
 * 	之后调用 PTC_Send PTC_Receive 发送或者接收数据，最后调用 PTC_Stop 结束本次通信。
 * 修改记录：
 * 	2020/5/28: 初版。 刘言。
***********************************************************************************/
#include "polsys.h"
#include "board.h"
#include "ptc.h"

pdata u16 mRes = 0;

static idata u16 mAdvLevel;		  // 高低电平分界值
static idata u8 mSendCycle = 100; // 半位周期，根据接收波特率计算

static void Send_1()
{
	PTC_DATA_OUT_H;
	PTC_DELAY_US(mSendCycle);
	PTC_DATA_OUT_L;
	PTC_DELAY_US(mSendCycle);
}

static void Send_0()
{
	PTC_DATA_OUT_L;
	PTC_DELAY_US(mSendCycle);
	PTC_DATA_OUT_H;
	PTC_DELAY_US(mSendCycle);
}

// 发送一个字节 先发送高位
static void SendByte(u8 dat)
{
	u8 i;

	// 发送起始位
	Send_0();

	for (i = 0; i < 8; i++)
	{
		if (dat & 0x80)
		{ // 发送1
			Send_1();
		}
		else
		{ // 发送0
			Send_0();
		}
		dat = dat << 1;
	}
	// 发送结束位
	Send_0();
}

// 发送一串数据
// buff: 待发送数据缓存（不包括长度和校验）
// len: 数据长度
// 注意：实际发出的数据会被加入长度和校验信息。
// 也就是按照数据包格式： |长度|--- 数据 ---|校验| 发送，长度和校验都为1字节。
void PTC_Send(const u8 *buff, u8 len)
{
	u8 i, checkAdd;

	checkAdd = GetSum_U8(buff, len); // 计算校验和
	// 发送数据包
	PTC_DELAY_US(150);
	PTC_READY_SEND;
	SendByte(len);			  //发长度,注意长度仅指数据区的长度，不包括校验码
	for (i = 0; i < len; i++) //发数据区
	{
		SendByte(buff[i]);
	}
	SendByte(checkAdd); //发校验和
	PTC_READY_RECEIVE;
}

// 接收数据。有隐患：当长度错误后可能会内存泄露！
// buff: 数据存放地址,不包含长度和校验
// len: 长度存放地址，1字节，长度为数据区长度（buff的长度，不包括长度本身和校验）
// 注意：限定了数据包的格式为 |长度|--- 数据 ---|校验| ，长度和校验都为1字节。
// 内部已经进行了数据包校验，即便校验错误， buff 中的数据也已经被填充，*len也已经被修改。
// 可自行决定校验错误的数据如何处理。
ptc_errcode_t PTC_Receive(u8 *buff, u8 *len)
{
	bit first = 0, last_level = 1, now_level = 1, level = 1, edge_level = 1;
	u8 i, now_bit = 1, now_byte = 0, byte_buff = 0, cycle, length;
	u16 adv;

	mAdvLevel = PTC_GET_ADV - PTC_DIFF_ADV; // 高电平分界值

	// 开始接收数据
	for (i = 0; i < 255; i++)
	{
		adv = PTC_GET_ADV;
		if (adv < mAdvLevel) //低电平
		{
			now_level = 0;
		}
		else //高电平
		{
			now_level = 1;
		}
		// 滤波
		if (last_level == now_level)
		{
			level = now_level;
		}
		last_level = now_level;

		// 边缘判断
		if (edge_level == 1 && level == 0) //下降沿
		{
			edge_level = level;
			mAdvLevel = adv + PTC_DIFF_ADV;
			if (first == 0)
			{
				i = 0; // 第一个下降沿
			}
			else
			{
				if (i > cycle) // 收到一个位 = 1
				{
					now_bit++;
					if (now_bit > 9)
					{
						return PTC_BUS_ERROR;
					}
					i = 0;
					byte_buff = byte_buff << 1;
					byte_buff |= 0x01;
				}
			}
		}
		else if (edge_level == 0 && level == 1) // 上升沿
		{
			edge_level = level;
			mAdvLevel = adv - PTC_DIFF_ADV;
			if (first == 0) // 第一个上升沿
			{
				first = 1;
				cycle = i + (i / 2); // 计算 3/4 周期
				mSendCycle = i * 10 + 15;
				i = 0;
			}
			else
			{
				if (i > cycle) // 收到一个位 = 0
				{
					i = 0;
					now_bit++;
					if (now_bit < 10)
						byte_buff = byte_buff << 1;
				}
			}
		}
		if (now_bit == 10)
		{
			now_bit = 0;
			if (now_byte == 0)
				length = byte_buff; // 第一个字节为长度
			else if (now_byte <= length)
				buff[now_byte - 1] = byte_buff;
			else // (now_byte > length) 一包数据接收完成 byte_buff （最后一个字节）为校验码
			{
				*len = length;
				// 验证校验是否正确
				if (GetSum_U8(buff, length) != byte_buff)
					return PTC_CHECK_NO_PASS;
				else
					return PTC_SUCCESS;
			}
			now_byte++;
		}
	}
	return PTC_RX_TIMEOUT;
}

/*
// 以下是收1个字节和收1个数据包分成2个函数，多占用了20字节FLASH，
// 收整个数据包之前必须先获取高低电平分界值，所以本文件仅提供收1个字节的接口也不行，所以暂时不用。
ptc_errcode_t PTC_ReceiveByte(u8 *byte)
{
	bit first=0, last_level=1, now_level=1, level=1, edge_level=1;
	u8 i, now_bit=1, byte_buff=0, cycle=0;
	u16 adv;

	// 开始接收数据
	for(i = 0; i < 255; i++)
	{
		adv = PTC_GET_ADV;
		if(adv < mAdvLevel)			//低电平
		{
			now_level = 0;
		}
		else	//高电平
		{
			now_level = 1;
		}
		// 滤波
		if(last_level == now_level)
		{
			level = now_level;
		}
		last_level = now_level;

		// 边缘判断
		if(edge_level == 1 && level == 0)   //下降沿
		{
			edge_level = level;
			mAdvLevel = adv + PTC_DIFF_ADV;
			if(first == 0)
			{
				i = 0;    // 第一个下降沿
			}
			else
			{
				if(i > cycle)  // 收到一个位 = 1
				{
					now_bit++;
					if(now_bit > 9) 
					{
						return PTC_BUS_ERROR;
					}
					i = 0;
					byte_buff=byte_buff<<1;
					byte_buff|=0x01;
				}
			}
		}
		else if(edge_level == 0 && level == 1)	// 上升沿
		{
			edge_level = level;
			mAdvLevel = adv - PTC_DIFF_ADV;
			if(first == 0)		// 第一个上升沿
			{
				first = 1;
				cycle = i + (i/2);	// 计算 3/4 周期
				mSendCycle = i * 10 + 15;
				i = 0;
			}
			else
			{
				if(i > cycle)  // 收到一个位 = 0
				{
					i = 0;
					now_bit++;
					if(now_bit < 10)
					byte_buff = byte_buff<<1;
				}
			}
		}
		if(now_bit == 10)	// 收到1个字节
		{
			*byte = byte_buff;
			return PTC_SUCCESS;	// 返回整个数据包的长度
		}
	}
	return PTC_RX_TIMEOUT;
}

//接收原始数据。buff:缓存地址，len长度地址
static ptc_errcode_t PTC_Receive(u8 *buff,u8 *len)
{
	ptc_errcode_t ret;
	u8 i;

	mAdvLevel = PTC_GET_ADV - PTC_DIFF_ADV;      // 高低电平分界值

	// 接收长度
	ret = PTC_ReceiveByte(len);
	if(ret != PTC_SUCCESS) return ret;
	// 接收数据 和 校验码
	for(i=0; i <= *len; i++)
	{
		ret = PTC_ReceiveByte(&buff[i]);
		if(ret != PTC_SUCCESS) return ret;
	}

	return PTC_SUCCESS;
}
*/

static void WeakOut()
{
	PTC_WEAK_OUT; //弱输出
}

//开始 同步，必须与Stop成对使用
ptc_errcode_t PTC_Start()
{
	u16 i, adv, th;

	PTC_REQUEST;   //申请硬件资源
	PTC_ENTER_CRI; //关中断，之后不要产生调度
	PTC_POWER_OFF;			 //彻底断电
	PTC_DELAY_MS(1);		 //延时1ms
	PTC_STRONG_OUT;			 //强输出
	PTC_DELAY_MS(1);		 //延时1ms
	mRes = PTC_UPDATA_RES(); // 更新雾化器阻值
	PTC_DELAY_MS(39);		 //延时30~60ms
	PTC_OUT_CHECK;			 //检查输出状态
	WeakOut();				 //弱输出
	PTC_SET_ADC;			 //配置ADC
	adv = PTC_GET_ADV;
	if (adv < PTC_ADV_2200MV)
		return PTC_NORMAL_ATM;
	th = PTC_GET_ADV + PTC_DIFF_ADV; //阈值
	for (i = 0; i < 10000; i++)		 //等待电压高于阈值（高电平跳变）,从机输出H表示同步成功
	{
		adv = PTC_GET_ADV;
		if (adv > th)
			break;
	}
	if (i == 10000)
		return PTC_WAIT_READY_TIMEOUT;
	return PTC_SUCCESS;
}

void PTC_Stop()
{
	PTC_EXIT_CRI; //开中断
	PTC_RELEASE;  //释放与还原资源
}

// 临时强供电
// ms: 临时强供电的时长，单位ms
// 返回错误码：如果短路会返回错误。
// 从机在收到某些命令后可能需要临时强供电来执行这个命令，通过调用此函数来临时强供电。
ptc_errcode_t PTC_TemporaryPower(u8 ms)
{
	PTC_STRONG_OUT;
	PTC_EXIT_CRI; //调用延时之前开启中断，使等待中的中断得到响应，不至于完全浪费这段时间
	PTC_DELAY_MS(ms);
	PTC_ENTER_CRI;
	WeakOut();
	return PTC_SUCCESS;
}
